package com.winchain.chainbill.tbill.biz.service.impl;

import com.shell.bee.acct.account.po.AcctBas;
import com.shell.bee.base.exception.UnCheckMsgException;
import com.shell.bee.base.utils.DateUtil;
import com.shell.bee.base.utils.MoneyUtil;
import com.shell.bee.base.utils.StringUtil;
import com.shell.bee.base.utils.SystemUtil;
import com.shell.bee.entity.mvc.RetMsg;
import com.shell.bee.persistence.annotation.Pagination;
import com.shell.bee.persistence.model.PageLimit;
import com.shell.framework.utils.CommonDataUtil;
import com.shell.framework.utils.MathsUtil;
import com.shell.framework.utils.SysDateUtil;
import com.winchain.chainbill.basic.cus.po.CusRec;
import com.winchain.chainbill.basic.cus.service.CusGrpService;
import com.winchain.chainbill.basic.invoice.rx.TBillInvoiceDeferedUploadMsgSubcriber;
import com.winchain.chainbill.basic.invoice.rx.TBillInvoiceDfrUploadSaveSubscriber;
import com.winchain.chainbill.basic.invoice.rx.TBillInvoiceDfrUploadUpdateSubscriber;
import com.winchain.chainbill.repayment.service.BillDctRpmtService;
import com.winchain.chainbill.repayment.vo.CbDctRpmtVo;
import com.winchain.chainbill.risk.utill.RateUtil;
import com.winchain.chainbill.tbill.base.dao.TbRecDao;
import com.winchain.chainbill.tbill.base.po.TbDct;
import com.winchain.chainbill.tbill.base.po.TbOpt;
import com.winchain.chainbill.tbill.base.po.TbRec;
import com.winchain.chainbill.tbill.base.po.TbSnd;
import com.winchain.chainbill.tbill.base.service.TbDctService;
import com.winchain.chainbill.tbill.base.service.TbRecService;
import com.winchain.chainbill.tbill.base.service.TbSndService;
import com.winchain.chainbill.tbill.basic.rx.TBillMoveSubscriber;
import com.winchain.chainbill.tbill.basic.rx.TBillOptSubscriber;
import com.winchain.chainbill.tbill.basic.rx.TBillUpdateSubscriber;
import com.winchain.chainbill.tbill.basic.service.TBillCheckService;
import com.winchain.chainbill.tbill.biz.TBill;
import com.winchain.chainbill.tbill.biz.TBillEvent;
import com.winchain.chainbill.tbill.biz.rx.TBillArtisanContractSponsorSubcriber;
import com.winchain.chainbill.tbill.biz.rx.TBillDctContractSponsorSubcriber;
import com.winchain.chainbill.tbill.biz.rx.TBillDctMsgSubscriber;
import com.winchain.chainbill.tbill.biz.rx.TBillDctQuotaSubscriber;
import com.winchain.chainbill.tbill.biz.rx.TBillDctSaveSerFeeSubscriber;
import com.winchain.chainbill.tbill.biz.rx.TBillDctSubscriber;
import com.winchain.chainbill.tbill.biz.rx.TBillSaveAttSubscriber;
import com.winchain.chainbill.tbill.biz.rx.TBillSndSubscriber;
import com.winchain.chainbill.tbill.biz.service.TBillService;
import com.winchain.chainbill.tbill.biz.vo.TBillQueryVo;
import com.winchain.chainbill.tbill.biz.vo.TbRecVo;
import com.winchain.chainbill.trade.service.BillAcctService;
import java.math.BigDecimal;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import rx.Observable;

@Service
public class TBillServiceImpl implements TBillService {
	@Autowired
	private TbRecDao tbRecDao;
	@Autowired
	private TbRecService tbRecService;
	@Autowired
	private TbSndService tbSndService;
	@Autowired
	private TbDctService tbDctService;
	@Autowired
	private CusGrpService cusGrpService;
	@Autowired
	private BillAcctService billAcctService;
	@Autowired
	private TBillCheckService tBillCheckService;
	@Autowired
	private TBillOptSubscriber tBillOptSubscriber;
	@Autowired
	private TBillMoveSubscriber tBillMoveSubscriber;
	@Autowired
	private TBillSndSubscriber tBillSndSubscriber;
	@Autowired
	private TBillDctSubscriber tBillDctSubscriber;
	@Autowired
	private TBillDctMsgSubscriber tBillDctMsgSubscriber;
	@Autowired
	private TBillDctSaveSerFeeSubscriber tBillDctSaveSerFeeSubscriber;
	@Autowired
	private TBillUpdateSubscriber tBillUpdateSubscriber;
	@Autowired
	private TBillDctQuotaSubscriber tBillDctQuotaSubscriber;
	@Autowired
	private TBillDctContractSponsorSubcriber tBillDctContractSponsorSubcriber;
	@Autowired
	private TBillArtisanContractSponsorSubcriber tBillArtisanContractSponsorSubcriber;
	@Autowired
	private TBillSaveAttSubscriber tBillSaveAttSubscriber;
	@Autowired
	private TBillInvoiceDfrUploadSaveSubscriber tBillInvoiceDfrUploadSaveSubscriber;
	@Autowired
	private TBillInvoiceDfrUploadUpdateSubscriber tBillInvoiceDfrUploadUpdateSubscriber;
	@Autowired
	private TBillInvoiceDeferedUploadMsgSubcriber tBillInvoiceDeferedUploadMsgSubcriber;
	@Autowired
	private BillDctRpmtService billDctRpmtService;

	public RetMsg saveTBill(TbRecVo tbRecVo) {
		boolean add = StringUtil.emptyAndNull(tbRecVo.getBillId());
		String msg = this.tBillCheckService.checkSnd(tbRecVo);
		if (StringUtil.notEmpty(msg)) {
			return new RetMsg("error", true, msg);
		}
		TbRec tbRec = initTbRec(tbRecVo, add);
		TbSnd tbSnd = initTbSnd(tbRecVo, add);

		TbOpt opt = getSndOpt(tbRec);
		opt.setRemark(tbRecVo.getRemark());
		opt.setOptCode("E001");

		TBill<TbDct> bill = new TBill();
		bill.setSource(null);
		bill.setRec(tbRec);
		TBillEvent<TbDct> event = new TBillEvent(bill);
		event.setValue("snd", tbSnd);
		event.setOperation(opt);
		Observable<TBillEvent<TbDct>> observers = Observable.just(event);
		if (add) {
			observers.subscribe(this.tBillOptSubscriber);
			observers.subscribe(this.tBillMoveSubscriber);
		} else {
			observers.subscribe(this.tBillUpdateSubscriber);
		}
		observers.subscribe(this.tBillSndSubscriber);
		return new RetMsg("success", false, "����������");
	}

	private TbSnd initTbSnd(TbRecVo tbRecVo, boolean add) {
		TbSnd tbSnd;
		if (add) {
			tbSnd = new TbSnd();
			tbSnd.setSndId(SystemUtil.getUUID());
			tbSnd.setBillId(tbRecVo.getBillId());
			tbSnd.setCreateTime(SysDateUtil.getSystemDateTime());
			tbSnd.setSndState("0");
		} else {
			tbSnd = this.tbSndService.selectByBillId(tbRecVo.getBillId());
		}
		tbSnd.setRemark(tbRecVo.getRemark());

		tbSnd.setSellerId(CommonDataUtil.getCurEntId());
		tbSnd.setSellerNm(tbRecVo.getSellerNm());
		tbSnd.setSellerAcctNo(tbRecVo.getSellerAcctNo());
		tbSnd.setSellerBankNm(tbRecVo.getSellerBankNm());

		tbSnd.setBuyerId(tbRecVo.getBuyerId());
		tbSnd.setBuyerNm(tbRecVo.getBuyerNm());
		tbSnd.setBuyerAcctNo(tbRecVo.getBuyerAcctNo());
		tbSnd.setBuyerBankNm(tbRecVo.getBuyerBankNm());

		tbSnd.setConfirmerId(tbSnd.getBuyerId());
		tbSnd.setConfirmerNm(tbSnd.getBuyerNm());
		tbSnd.setConfirmerAcctNo(tbSnd.getBuyerAcctNo());
		tbSnd.setConfirmerBankNm(tbSnd.getBuyerBankNm());
		tbSnd.setConfirmerBankNo(tbRecVo.getConfirmerBankNo());
		return tbSnd;
	}

	private TbRec initTbRec(TbRecVo tbRecVo, boolean add) {
		TbRec tbRec;
		if (add) {
			tbRec = new TbRec();
			String billId = SystemUtil.getUUID();
			tbRec.setBillId(billId);
			tbRec.setBillState("S000");
			tbRec.setHldrLvl(Integer.valueOf(-1));
			tbRec.setOptSeq(Integer.valueOf(-1));
			tbRec.setRootId(billId);

			tbRecVo.setBillId(billId);
		} else {
			tbRec = (TbRec) this.tbRecDao.selectByPrimaryKey(tbRecVo.getBillId());
		}
		tbRec.setBillCode(tbRecVo.getBillCode());
		String billType = String.valueOf(tbRec.getBillCode().charAt(0));
		tbRec.setBillType(billType);
		tbRec.setBillCode(tbRecVo.getBillCode());
		tbRec.setBillAmt(tbRecVo.getBillAmt());
		tbRec.setSndDate(tbRecVo.getSndDate());
		tbRec.setExpDate(tbRecVo.getExpDate());
		tbRec.setGrpId(tbRecVo.getGrpId());
		tbRec.setGrpNm(tbRecVo.getGrpNm());
		return tbRec;
	}

	@Pagination
	public List<TbRecVo> queryTBills(PageLimit limit, TBillQueryVo tBillQueryVo) {
		if (StringUtil.notEmpty(tBillQueryVo.getBuyerNm())) {
			tBillQueryVo.setBuyerNm(StringUtil.getParameterLike(tBillQueryVo.getBuyerNm()));
		}
		if (StringUtil.notEmpty(tBillQueryVo.getBillCode())) {
			tBillQueryVo.setBillCode(StringUtil.getParameterLike(tBillQueryVo.getBillCode()));
		}
		tBillQueryVo.setCusId(CommonDataUtil.getCurEntId());
		return this.tbRecDao.queryTBills(tBillQueryVo);
	}

	public RetMsg dctImmediately(TbRecVo tbRecVo) {
		return dct(tbRecVo, true);
	}

	public TbRecVo getDetail(String billId) {
		TbRecVo vo = this.tbRecDao.getDetail(billId);
		String cusId = CommonDataUtil.getCurEntId();
		AcctBas acct = this.billAcctService.getAcctBas(cusId, null, "00");
		vo.setAcct(acct);
		return vo;
	}

	public RetMsg dct(TbRecVo tbRecVo, boolean firstDct) {
		String msg = this.tBillCheckService.checkSnd(tbRecVo);
		if (StringUtil.notEmpty(msg)) {
			return new RetMsg("error", true, msg);
		}
		msg = this.tBillCheckService.checkDct(tbRecVo);
		if (StringUtil.notEmpty(msg)) {
			return new RetMsg("error", true, msg);
		}
		TbRec tbRec = initTbRec(tbRecVo, firstDct);
		TbSnd tbSnd = initTbSnd(tbRecVo, firstDct);
		TbDct tbDct = initTbDct(tbRecVo, tbRec, tbSnd);

		String eventCode = "E002";

		TbOpt opt = getSndOpt(tbRec);
		opt.setRemark(tbRecVo.getRemark());
		opt.setOptCode(eventCode);
		opt.setLimDate(tbDct.getDctDate());

		TBill<TbDct> bill = new TBill();
		bill.setSource(tbDct);
		bill.setRec(tbRec);
		TBillEvent<TbDct> event = new TBillEvent(bill);
		event.setOperation(opt);
		event.setValue("snd", tbSnd);
		event.setValue("quotaOptType", "qt_blk");
		event.setValue("phoneCode", tbRecVo.getPhoneCode());
		event.setValue("location", tbRecVo.getLocation());
		event.setValue("isIntPayByBuyer", Boolean.valueOf(tbRecVo.isIntPayByBuyer()));
		event.setValue("contractFileIds", tbRecVo.getContractFileIds());
		event.setValue("attInvoicesJson", tbRecVo.getAttInvoicesJson());

		Observable<TBillEvent<TbDct>> observers = Observable.just(event);

		observers.subscribe(this.tBillDctContractSponsorSubcriber);
		observers.subscribe(this.tBillArtisanContractSponsorSubcriber);

		observers.subscribe(this.tBillOptSubscriber);
		observers.subscribe(this.tBillMoveSubscriber);
		observers.subscribe(this.tBillSndSubscriber);
		observers.subscribe(this.tBillDctSubscriber);
		observers.subscribe(this.tBillDctQuotaSubscriber);
		observers.subscribe(this.tBillDctSaveSerFeeSubscriber);
		observers.subscribe(this.tBillSaveAttSubscriber);
		observers.subscribe(this.tBillInvoiceDfrUploadSaveSubscriber);

		observers.subscribe(this.tBillDctMsgSubscriber);
		return new RetMsg("success", false, "������������������3����������������������������������");
	}

	public void confirmReceivePayment(String billId, CbDctRpmtVo dctRpmtVo) {
		TbRec rec = (TbRec) this.tbRecDao.selectByPrimaryKey(billId);
		TbSnd snd = this.tbSndService.selectByBillId(billId);
		TbDct dct = this.tbDctService.getDct(billId, "2");
		TbOpt opt = new TbOpt();
		opt.setCurState(rec.getBillState());
		opt.setOptCode("E009");
		opt.setLimDate("");
		opt.setRemark("��������");
		TBill<TbDct> bill = new TBill();
		bill.setSource(dct);
		bill.setRec(rec);

		snd.setSndState("1");
		TBillEvent<TbDct> event = new TBillEvent(bill);
		event.setOperation(opt);
		event.setValue("snd", snd);
		event.setValue("quotaOptType", "tb_res");
		Observable<TBillEvent<TbDct>> observers = Observable.just(event);
		observers.subscribe(this.tBillOptSubscriber);
		observers.subscribe(this.tBillMoveSubscriber);
		observers.subscribe(this.tBillSndSubscriber);
		observers.subscribe(this.tBillDctQuotaSubscriber);

		this.billDctRpmtService.saveDctRpmt(dctRpmtVo);
	}

	public void uploadInvoices(String dctId, String attInvoicesJson) {
		TbDct dct = (TbDct) this.tbDctService.selectByKey(dctId);
		TbRec rec = (TbRec) this.tbRecDao.selectByPrimaryKey(dct.getPrntId());
		TBill<TbDct> bill = new TBill();
		bill.setRec(rec);
		bill.setSource(dct);
		TBillEvent<TbDct> event = new TBillEvent(bill);
		event.setValue("attInvoicesJson", attInvoicesJson);
		event.setValue("invType", "5");
		event.setValue("bizId", ((TbDct) bill.getSource()).getDctId());
		event.setValue("dfrState", "2");
		Observable<TBillEvent<TbDct>> observers = Observable.just(event);
		observers.subscribe(this.tBillSaveAttSubscriber);
		observers.subscribe(this.tBillInvoiceDfrUploadUpdateSubscriber);
		observers.subscribe(this.tBillInvoiceDeferedUploadMsgSubcriber);
	}

	@Pagination
	public List<TbRecVo> selectUnuploadInvoiceListByPage(PageLimit limit, TBillQueryVo tBillQueryVo) {
		tBillQueryVo.setCusId(CommonDataUtil.getCurEntId());
		if (StringUtil.notEmpty(tBillQueryVo.getBillCode())) {
			tBillQueryVo.setBillCode(StringUtil.getParameterLike(tBillQueryVo.getBillCode()));
		}
		if (StringUtil.notEmpty(tBillQueryVo.getBuyerNm())) {
			tBillQueryVo.setBuyerNm(StringUtil.getParameterLike(tBillQueryVo.getBuyerNm()));
		}
		List<TbRecVo> list = this.tbRecDao.selectUnuploadInvoiceList(tBillQueryVo);
		String curDate = SysDateUtil.getSystemDate();
		for (TbRecVo rec : list) {
			if (StringUtil.notEmpty(rec.getDfrUploadExpDate())) {
				int days = SysDateUtil.getDaysBetween(curDate, rec.getDfrUploadExpDate());
				rec.setRemainInvDfrDays(days);
			}
		}
		return list;
	}

	public boolean existBillCode(String billId, String billCode) {
		TbRec rec = new TbRec();
		rec.setBillCode(billCode);
		rec = (TbRec) this.tbRecService.selectOne(rec);
		return (rec != null) && (!rec.getBillId().equals(billId));
	}

	@Pagination
	public List<TbRecVo> queryTBillsForMgt(PageLimit limit, TBillQueryVo tBillQueryVo) {
		String buyerNm = tBillQueryVo.getBuyerNm();
		if (StringUtil.notEmpty(buyerNm)) {
			buyerNm = StringUtil.getParameterLike(buyerNm);
			tBillQueryVo.setBuyerNm(buyerNm);
		}
		String sellerNm = tBillQueryVo.getSellerNm();
		if (StringUtil.notEmpty(sellerNm)) {
			sellerNm = StringUtil.getParameterLike(sellerNm);
			tBillQueryVo.setSellerNm(sellerNm);
		}
		String billCode = tBillQueryVo.getBillCode();
		if (StringUtil.notEmpty(billCode)) {
			billCode = StringUtil.getParameterLike(billCode);
			tBillQueryVo.setBillCode(billCode);
		}
		return this.tbRecDao.queryTBillsForMgt(tBillQueryVo);
	}

	private TbDct initTbDct(TbRecVo tbRecVo, TbRec tbRec, TbSnd tbSnd) {
		TbDct dct = new TbDct();
		dct.setDctId(SystemUtil.getUUID());
		dct.setBillId(tbRec.getBillId());
		dct.setPrntId(tbRec.getBillId());
		dct.setDctState("1");
		dct.setHldrLvl(tbRec.getHldrLvl());
		dct.setCreatorId(CommonDataUtil.getUserId());
		dct.setCreateTime(DateUtil.getSystemDateTime());
		dct.setPmtState("0");
		dct.setExpDate(tbRec.getExpDate());

		dct.setUploadInvoiceDefer(tbRecVo.isUploadInvoiceDefer());
		dct.setCusDfrDays(tbRecVo.getCusDfrDays());

		dct.setSellerId(tbSnd.getSellerId());
		dct.setSellerNm(tbSnd.getSellerNm());
		dct.setSellerAcct(tbSnd.getSellerAcctNo());
		if (tbRecVo.isIntPayByBuyer()) {
			dct.setBuyerId(tbSnd.getBuyerId());
			dct.setBuyerNm(tbSnd.getBuyerNm());
			dct.setBuyerAcct(tbSnd.getBuyerAcctNo());
		} else {
			dct.setBuyerId(tbSnd.getSellerId());
			dct.setBuyerNm(tbSnd.getSellerNm());
			dct.setBuyerAcct(tbSnd.getSellerAcctNo());
		}
		List<CusRec> finners = this.cusGrpService.findGroupEnts(tbRec.getGrpId(), "FIN_BUYER");
		if ((finners == null) || (finners.size() == 0)) {
			throw new UnCheckMsgException("��������������");
		}
		CusRec finner = (CusRec) finners.get(0);
		dct.setFinId(finner.getCusId());
		dct.setFinNm(finner.getCusName());
		AcctBas finAcct = this.billAcctService.getAcctBas(finner.getCusId(), null, "00");
		dct.setFinAcct(finAcct.getAcctNo());

		tbRec.setFinId(finner.getCusId());
		tbRec.setFinNm(finner.getCusName());

		String dctDate = tbRecVo.getDctDate();
		if (StringUtil.emptyAndNull(dctDate)) {
			dctDate = SysDateUtil.getSystemDate();
		}
		dct.setDctDate(dctDate);
		dct.setDctDays(Integer.valueOf(DateUtil.getDaysBetween(dctDate, tbRec.getExpDate())));
		dct.setDctAmt(tbRecVo.getBillAmt());
		dct.setSerFee(tbRecVo.getSerFee());

		String dctFixedRate = RateUtil.getFixedRate(tbRec.getGrpId(), dct.getFinId(), "tb_dct_fixed_rate");
		if (StringUtil.empty(dctFixedRate)) {
			throw new UnCheckMsgException("������������������������������");
		}
		BigDecimal fr = new BigDecimal(dctFixedRate);

		dct.setFixedRate(fr);
		dct.setVarRate(BigDecimal.ZERO);
		dct.setEntVarRate(BigDecimal.ZERO);

		BigDecimal sellInt = MathsUtil.mathDctInterest(dct.getDctAmt(), fr, dct.getDctDays().longValue(), "2");

		dct.setDctInt(sellInt);
		dct.setRateMode("2");
		BigDecimal actualAmt = dct.getDctAmt().subtract(dct.getDctInt());
		if (dct.getDeductInt() != null) {
			actualAmt = actualAmt.add(dct.getDeductInt());
		}
		if (!MoneyUtil.biggerThanZero(actualAmt)) {
			throw new UnCheckMsgException("��������������������0");
		}
		return dct;
	}

	private TbOpt getSndOpt(TbRec bill) {
		TbOpt opt = new TbOpt();
		opt.setCurState(bill.getBillState());
		opt.setOptCode("E001");
		opt.setLimDate(bill.getExpDate());
		return opt;
	}
}
